package edu.cmu.cs211.snake.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.Random;

import org.junit.Test;

import edu.cmu.cs211.snake.util.ArrayQueue;

public class ArrayQueueTest {
	
	/**
	 * Test creating a queue, and make sure that there are no elements
	 * in there for a new, blank one.  Make sure:
	 * 	<p>	1) size() == 0 </p>
	 * 	<p>	2) peek() == null </p>
	 * 	<p>	3) poll() == null </p>
	 */
	@Test (timeout = 1000)
	public void createQueueTest ()
	{
		Queue<Object> q = new ArrayQueue<Object> ();
		assertEquals ("Create new queue, call size()", 0, q.size ());
		assertEquals ("Create new queue, call peek()", null, q.peek ());
		assertEquals ("Create new queue, call poll()", null, q.poll ());
	}

	/**
	 * Tests basic offer functionality to make sure it returns true
	 * and changes the size
	 */
	@Test (timeout = 1000)
	public void offerTest ()
	{
		/* test offer null */
		Queue <Object> q = new ArrayQueue <Object> ();
		assertEquals ("Create queue, call offer(null)", true, q.offer (null));
		assertEquals ("Call size() after offer(null)", 1, q.size ());
		
		/* test offer and size change */
		Queue <Integer> q1 = new ArrayQueue <Integer> ();
		assertEquals ("Create queue, call offer(1)", true, q1.offer (1));
		assertEquals ("Create queue, call offer(2)", true, q1.offer (2));
		assertEquals ("Create queue, call offer(3)", true, q1.offer (3));
		assertEquals ("Create queue, call offer(4)", true, q1.offer (4));
		assertEquals ("Create queue, call offer(5)", true, q1.offer (5));
		assertEquals ("Create queue, call offer(1)", true, q1.offer (6));
		assertEquals ("Create queue, call offer(2)", true, q1.offer (7));
		assertEquals ("Create queue, call offer(3)", true, q1.offer (8));
		assertEquals ("Create queue, call offer(4)", true, q1.offer (9));
		assertEquals ("Create queue, call offer(5)", true, q1.offer (10));
		assertEquals ("Create queue, call offer(1)", true, q1.offer (11));
		assertEquals ("Create queue, call offer(2)", true, q1.offer (12));
		assertEquals ("Create queue, call offer(3)", true, q1.offer (13));
		assertEquals ("Create queue, call offer(4)", true, q1.offer (14));
		assertEquals ("Create queue, call offer(5)", true, q1.offer (15));
		assertEquals ("Create queue, call offer(3)", true, q1.offer (16));
		assertEquals ("Create queue, call offer(4)", true, q1.offer (17));
		assertEquals ("Create queue, call offer(5)", true, q1.offer (18));
		assertEquals ("Create queue, call offer(1)", true, q1.offer (111));
		assertEquals ("Create queue, call offer(2)", true, q1.offer (112));
		assertEquals ("Create queue, call offer(3)", true, q1.offer (113));
		assertEquals ("Create queue, call offer(4)", true, q1.offer (114));
		assertEquals ("Create queue, call offer(5)", true, q1.offer (115));
		assertEquals ("Create queue, call offer(1)", true, q1.offer (116));
		assertEquals ("Create queue, call offer(2)", true, q1.offer (117));
		assertEquals ("Create queue, call offer(3)", true, q1.offer (118));
		assertEquals ("Create queue, call offer(4)", true, q1.offer (119));
		assertEquals ("Create queue, call offer(5)", true, q1.offer (1110));
		assertEquals ("Create queue, call offer(1)", true, q1.offer (1111));
		assertEquals ("Create queue, call offer(2)", true, q1.offer (1112));
		assertEquals ("Create queue, call offer(3)", true, q1.offer (1113));
		assertEquals ("Create queue, call offer(4)", true, q1.offer (1114));
		assertEquals ("Create queue, call offer(5)", true, q1.offer (1115));
		assertEquals ("Create queue, call offer(3)", true, q1.offer (1116));
		assertEquals ("Create queue, call offer(4)", true, q1.offer (1117));
		assertEquals ("Create queue, call offer(5)", true, q1.offer (1118));
		assertEquals ("Create queue, call offer(5)", true, q1.offer (37));
		assertEquals ("Call size() after offer(null)", 37, q1.size ());
		
		Iterator<Integer> itr = q1.iterator ();
		while(itr.hasNext())
			System.out.println(itr.next());

	}

	/**
	 * Test corner case for iterator
	 */
	@Test ( expected = NoSuchElementException.class)
	public void iterateNSEExceptionCornerCase ()
	{
		Queue<Integer> q = new ArrayQueue <Integer>();
		for(int i = 0; i < 10; i ++)
			q.offer(i);
		
		for(int i = 0; i < 10; i ++)
			q.poll();

		Iterator<Integer> itr = q.iterator ();
		System.out.print("size of q is " + q.size());
		assertFalse ("Call hasNext() on iterator returned by new queue", itr.hasNext());
		
		// the following line should throw the NSEException
		itr.next ();
	}
	
	/**
	 * This is an example of a randomized stress test. The goal here is to bring
	 * out corner cases. It's also important to do more targeted testing. Try to
	 * test odd things that might happen. For example, the circular behavior of
	 * the array queue is a good place to find issues.
	 */
	@Test
	public void randomAddRemoveTest ()	{
		Random r = new Random ();
		Queue <Object> mine = new ArrayQueue <Object> ();
		Queue <Object> compare = new LinkedList <Object> ();
		
		int NTESTS = 10000;
		
		for (int i = 0; i < NTESTS; i ++) {
			if (compare.size() == 0 || r.nextBoolean()) {
				Object o = new Object ();
				mine.offer (o);
				compare.offer (o);
			} else {
				assertEquals ("object from peek()", compare.peek(), mine.peek());
				assertEquals ("size after peek()",  compare.size(), mine.size());
				assertEquals ("object from poll()", compare.poll(), mine.poll());
			}
			
			assertEquals ("size after operations", compare.size(), mine.size());
		}
	}
	
	/**
	 * More targeted test for the resize event in your array queue.  Please look at 
	 * the testing document for more ideas...
	 *
	 */
	@Test
	public void resizeTest () {
		Queue <Integer> q = new ArrayQueue <Integer> ();
		for(int i = 0; i < 42; i++)
		{
			q.offer(i);
		}
		assertEquals ("Test resize", 42, q.size ());
	}
}
